package analysis;

/**
 * A class that keeps the prefix and pump part of an input string to a vulnerable regular expression
 * @author N. H. Weideman
 *
 */
public class ExploitString {
	
	private final static int VERBATIM_CHAR_MIN = 33; /* ! */
	private final static int VERBATIM_CHAR_MAX = 126; /* ~ */
		
	private final int degree;
	public int getDegree() {
		return degree;
	}
	
	private final String separators[];
	public String getSeparatorByDegreeVisual(int degree) {
		return visualiseString(separators[degree]);
	}
	
	public final String[] getSeparators() {
		String[] separators = new String[degree];
		for (int i = 0; i < degree; i++) {
			separators[i] = this.separators[i];
		}
		return separators;
	}
	
	public String getPrefixVisual() {
		return visualiseString(separators[0]);
	}
	
	public String getPrefix() {
		return separators[0];
	}
	
	public String getSeparatorByDegree(int degree) {
		return separators[degree];
	}
	
	private final String pumps[];
	public String getPumpByDegreeVisual(int degree) {
		return visualiseString(pumps[degree]);
	}
	
	public final String[] getPumps() {
		String[] pumps = new String[degree];
		for (int i = 0; i < degree; i++) {
			pumps[i] = this.pumps[i];
		}
		return pumps;
	}
	
	public String getPumpByDegree(int degree) {
		return pumps[degree];
	}
	
	private final String suffix;
	public String getSuffixVisual() {
		return visualiseString(suffix);
	}
	
	public String getSuffix() {
		return suffix;
	}
	
	
	public ExploitString(String prefix, String pump, String suffix) {
		this.degree = 0;
		separators = new String[1];
		separators[0] = prefix;
		pumps = new String[1];
		pumps[0] = pump;
		this.suffix = suffix;
	}
	
	public ExploitString(String[] separators, String[] pumps, String suffix) {
		this.degree = pumps.length;
		if (separators.length != pumps.length) {
			throw new IllegalArgumentException("There must be the same number of separators as pumps");
		}
		this.pumps = new String[degree];
		this.separators = new String[degree];
		for (int i = 0; i < degree; i++) {
			if (separators[i] == null) {
				throw new IllegalArgumentException("Null separators not allowed.");
			}
			if (pumps[i] == null) {
				throw new IllegalArgumentException("Null pumps not allowed.");
			}
			this.pumps[i] = pumps[i];
			this.separators[i] = separators[i];
		}
		
		this.suffix = suffix;
		
	}

	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		if (degree <= 0) {
			/* EDA exploit string */
			sb.append(visualiseString(separators[0]));
			sb.append(visualiseString(pumps[0]));
			sb.append("...");
			sb.append(visualiseString(pumps[0]));
		} else {
			for (int i = 0; i < degree; i++) {
				sb.append(visualiseString(separators[i]));
				sb.append(visualiseString(pumps[i]));
				sb.append("...");
				sb.append(visualiseString(pumps[i]));
			}
		}
		String visibleSuffix = visualiseString(suffix);
		sb.append(visibleSuffix);
		return sb.toString();
		
	}
	
	public static String visualiseString(String s) {
		StringBuilder sb = new StringBuilder();
		char sArr[] = s.toCharArray();
		for (int i = 0; i < sArr.length; i++) {
			int c = (int) sArr[i];
			if (c >= VERBATIM_CHAR_MIN && c <= VERBATIM_CHAR_MAX) {
				sb.append(sArr[i]);
			} else {
				if (c < 256) {
					sb.append(String.format("\\x%02x", c));
				} else {
					sb.append(String.format("\\x{%02x}", c));
				}
				
			}
		}
		return sb.toString();
	}
	
	@Override
	public boolean equals(Object o) {
		if (o == null) {
			return false;
		}
		if (!o.getClass().isAssignableFrom(this.getClass())) {
			return false;
		}
		ExploitString es = (ExploitString) o;
		
		boolean testDegree = degree == es.degree;
		
		boolean testPumps = false;
		boolean testSeparators = false;
		if (pumps == null) {
			testPumps = es.pumps == null;
		} else {
			if (!testDegree) {
				return false;
			}
			for (int i = 0; i < degree; i++) {
				if (!separators[i].equals(es.separators[i])) {
					testSeparators = false;
					break;
				}
				if (!pumps[i].equals(es.pumps[i])) {
					testPumps = false;
					break;
				}
			}
		}
		
		boolean testSuffix = false;
		if (suffix == null) {
			testSuffix = es.suffix == null;
		} else {
			testSuffix = suffix.equals(es.suffix);
		}
		
		boolean condition = testDegree && testPumps && testSeparators && testSuffix;
		return condition;
	}

	@Override
	public int hashCode()  {
		return toString().hashCode();
	}

}
